package me.zhengjie.modules.quartz.task;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.qiaofang.applet.common.util.RedisKeyUtil;
import com.qiaofang.applet.common.util.RedisUtil;
import com.qiaofang.applet.service.member.domain.WxFollowUsers;
import com.qiaofang.applet.service.member.service.WxFollowUsersService;
import com.qiaofang.applet.service.member.service.dto.PullWxFollowDto;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.quartz.task.dto.BatchPullUserInfoDto;
import me.zhengjie.modules.quartz.task.dto.OpenIdDto;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: youqing.yang
 * @Create: 2021/06/22 10:07
 * @Description:
 **/
@Slf4j
@Component
public class PullWXFollowUsersTask {

    /**
     * 获取用户openId列表
     * GET
     * https://developers.weixin.qq.com/doc/offiaccount/User_Management/Getting_a_User_List.html
     */
    private static final String USER_URL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID";

    /**
     * 批量获取关注用户详情
     * POST
     * https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId
     */
    private static final String BATCH_GET_USER_DETAIL_URL = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN";

    private static RestTemplate restTemplate = new RestTemplate();

    private static final Integer pageSize = 10000;

    @Autowired
    private WxFollowUsersService wxFollowUsersService;


    /**
     * 不知道后续有没有公司需要,这里也做成一个同样的
     * 集团code、公众号appid、Secret
     *
     * @param jsonParam
     */
    public void run(String jsonParam) {
        log.info("----首次同步微信公众号用户----");
        PullWxFollowDto paramDto = JSON.parseObject(jsonParam, PullWxFollowDto.class);
        if (paramDto == null) {
            log.info("param error => dto is null:{}", jsonParam);
            return;
        }
        if (StringUtils.isBlank(paramDto.getCorporationCode()) ||
                StringUtils.isBlank(paramDto.getAppId()) || StringUtils.isBlank(paramDto.getAppSecret())) {
            log.info("param error => paramDto param has null param:{}", jsonParam);
            return;
        }
        long l = System.currentTimeMillis();
        log.info("PullWXFollowUsersTask begin");
        pullAllFollowUserOpenId(paramDto);
        log.info("同步:{}公司微信公众号关注用户耗时：{}ms", paramDto.getCorporationCode(), System.currentTimeMillis() - l);

        log.info("PullWXFollowUsersTask end");

    }

    /**
     * 拉取所有关注者的openId
     *
     * @param paramDto
     */
    private void pullAllFollowUserOpenId(PullWxFollowDto paramDto) {

        /**
         * 拉取全量关注用户
         * 一次对多拉取10000
         * 多次拉取，后续添加next_openid
         */
        String accessToken = wxFollowUsersService.getAccessToken(paramDto);
        if (StringUtils.isBlank(accessToken)) {
            accessToken = wxFollowUsersService.getAccessToken(paramDto);
        }

        String user_url = StrUtil.subBefore(USER_URL.replace("ACCESS_TOKEN", accessToken), "&next_openid=NEXT_OPENID", false);
        JSONObject userObject = null;
        try {
            userObject = restTemplate.getForObject(user_url, JSONObject.class);
        } catch (RestClientException e) {
            log.error("获取微信关注用户openid error：{}", e);
        }
        if (userObject == null) {
            log.info("userObject result is null");
            return;

        }
        int totalCount = userObject.getInteger("total");
        log.info("{}公司，微信公众号一共需拉取：{}位用户", paramDto.getCorporationCode(), totalCount);
        if (totalCount == 0) {
            log.info("userObject totalCount is 0");
            return;
        }
        List<String> userOpenIds = new ArrayList<>();
        JSONObject data = userObject.getJSONObject("data");
        List<String> firstPull = JSONObject.parseArray(data.getJSONArray("openid").toJSONString(), String.class);
        userOpenIds.addAll(firstPull);
        pullFollowUserDetailAndSaveDB(userOpenIds, paramDto);

        //判断多次拉取
        int totalPage = getTotalPage(totalCount);
        if (totalPage < 2) {
            return;
        }

        String batchPullUrl;
        JSONObject batchUserObject = null;
        //循环拉取
        String next_openid = userObject.getString("next_openid");

        List<String> userOpenIdList = new ArrayList<>();

        for (int startPage = 2; startPage <= totalPage; startPage++) {
            batchPullUrl = USER_URL.replace("ACCESS_TOKEN", accessToken).replace("NEXT_OPENID", next_openid);
            try {
                batchUserObject = restTemplate.getForObject(batchPullUrl, JSONObject.class);
            } catch (RestClientException e) {
                log.error("循环拉取openid error：{}", e);
            }
            if (batchUserObject != null) {
                JSONObject dataResult = batchUserObject.getJSONObject("data");
                List<String> firstPullResult = JSONObject.parseArray(dataResult.getJSONArray("openid").toJSONString(), String.class);
                userOpenIdList.addAll(firstPullResult);
                next_openid = batchUserObject.getString("next_openid");
            }
        }
        pullFollowUserDetailAndSaveDB(userOpenIdList, paramDto);
    }

    /**
     * 保存微信关注用户详细信息到db
     *
     * @param openidList
     * @param pullWxFollowDto
     */
    private void pullFollowUserDetailAndSaveDB(List<String> openidList, PullWxFollowDto pullWxFollowDto) {
        String accessToken = wxFollowUsersService.getAccessToken(pullWxFollowDto);
        if (StringUtils.isBlank(accessToken)) {
            accessToken = wxFollowUsersService.getAccessToken(pullWxFollowDto);
        }
        log.info("accessToken:{}", accessToken);
        String url = BATCH_GET_USER_DETAIL_URL.replace("ACCESS_TOKEN", accessToken);
        BatchPullUserInfoDto batchPullUserInfoDto = new BatchPullUserInfoDto();
        List<List<String>> partition = Lists.partition(openidList, 100);
        log.info("单次需要批量拉取：{}次", partition.size());
        for (List<String> strings : partition) {
            List<OpenIdDto> openIdDtos = strings.stream().map(item -> {
                OpenIdDto openIdDto = new OpenIdDto();
                openIdDto.setOpenid(item);
                return openIdDto;
            }).collect(Collectors.toList());
            batchPullUserInfoDto.setUser_list(openIdDtos);
            JSONObject jsonObject = null;
            try {
                jsonObject = restTemplate.postForObject(url, new HttpEntity<>(batchPullUserInfoDto, new HttpHeaders()), JSONObject.class);
            } catch (RestClientException e) {
                log.error("batch pull user detail list error:{}", e);
            }
            if (jsonObject != null) {
                JSONArray user_info_list = jsonObject.getJSONArray("user_info_list");
                if (CollectionUtils.isEmpty(user_info_list)) {
                    log.info("user_info_list is null restTemplate error:{}", JSONObject.toJSONString(jsonObject));
                    return;
                }
                List<WxFollowUsers> wxFollowUsers = JSONObject.parseArray(user_info_list.toJSONString(), WxFollowUsers.class);
                wxFollowUsers.forEach(wxFollowUser -> {
                    wxFollowUser.setCorporationCode(pullWxFollowDto.getCorporationCode());
                });
                wxFollowUsersService.saveAll(wxFollowUsers);
            }
        }
    }

    /**
     * 拉取次数
     *
     * @param totalRecord
     * @return
     */
    private int getTotalPage(int totalRecord) {
        return (totalRecord + pageSize - 1) / pageSize;
    }

}
